//
// Created by daidai on 2022/12/11.
//

#include <cstring>
#include "VideoChannel.h"


VideoChannel::VideoChannel() {
    this->safeQueue = new SafeQueue<RTMPPacket *>;
}

int VideoChannel::initVideoCodec(int width1, int height1, int fps1, int bitrate1) {

    this->width = width1;
    this->height = height1;
    this->fps = fps1;
    this->bitrate = bitrate1;
    // 定义参数
    x264_param_t x264Param; // 如果指针 需要分配内存默认

    //TODO 速度和质量的平衡，有ultrafast、superfast、veryfast、faster、
    // fast、medium、slow、slower、veryslow、placebo这10个选项，从快到慢
    // ultrafast编码速度最快，但压缩率低，生成的文件更大，placebo则正好相反。x264所取的默认值为medium

    //TODO tune的参数主要配合视频类型和视觉优化的参数，或特别的情况。如果视频的内容符合其中一个可用的调整值又或者有其中需要，
    // 则可以使用此选项，否则建议不使用（如tune grain是为高比特率的编码而设计的）。
    // tune的值有： film： 电影、真人类型； animation： 动画； grain： 需要保留大量的grain时用；
    // stillimage： 静态图像编码时使用； psnr： 为提高psnr做了优化的参数； ssim： 为提高ssim做了优化的参数；
    // fastdecode： 可以快速解码的参数； zerolatency：零延迟，用在需要非常低的延迟的情况下，比如电视电话会议的编码
    x264_param_default_preset(&x264Param, "ultrafast", "zerolatency");

    // 编码等级
    x264Param.i_level_idc = 32;

    //显示格式
    x264Param.i_csp = X264_CSP_I420;
    x264Param.i_width = width;
    x264Param.i_height = height;

    // 二 I 帧 之间的 B帧树木
    x264Param.i_bframe = 0;

    // cpu ABR 折中
    x264Param.rc.i_rc_method = X264_RC_ABR;

    // k 单位 比特率
    x264Param.rc.i_bitrate = this->bitrate / 1024;

    // 帧率  分母
    x264Param.i_fps_num = this->fps;
    // 分子
    x264Param.i_fps_den = 1;

    // 时间基
    x264Param.i_timebase_num = x264Param.i_fps_num;
    x264Param.i_timebase_den = x264Param.i_fps_den;

    // 计算帧间距的依据, 该设置表示使用 fps 帧率计算帧间距
// 两帧之间间隔多少 fps
// 也可以使用时间戳计算帧间距
    x264Param.b_vfr_input = 0;

    //秒开  I 帧
    x264Param.i_keyint_max = 2 * this->fps;

    // sps  pps 重复输出1  0 输出一次
    x264Param.b_repeat_headers = 1;

    // 多线程

    x264Param.i_threads = 1;

    x264_param_apply_profile(&x264Param, "baseline");

    x264_encoder = x264_encoder_open(&x264Param);

    pic_in = new x264_picture_t;

    x264_picture_alloc(pic_in, X264_CSP_I420, this->width, this->height);
    // 初始化完毕

    return 0;
}

void VideoChannel::encodeData(int8_t *data) {

    // 开始编码
    // 把y 拷贝进去
    memcpy(pic_in->img.plane[0], data, width * height);

    // 都 是 1/4
    int uvsize = width * height * 1 / 4;

    for (int i = 0; i < uvsize; ++i) {
        // u  nv12
        *(pic_in->img.plane[1] + i) = *(data + width * height + i * 2 + 1);
        *(pic_in->img.plane[2] + i) = *(data + width * height + i * 2);
    }
    // 然后搞pps sps
    // h264 一帧
    x264_nal_t *pp_nals;
    //编码出几个nalu
    int pi_nal;
    //编码出得数据
    x264_picture_t pic_out;
    //编码出得数据h264
    x264_encoder_encode(x264_encoder, &pp_nals, &pi_nal, pic_in, &pic_out);

    uint8_t sps[100];
    uint8_t pps[100];
    int sps_len, pps_len;
    for (int i = 0; i < pi_nal; ++i) {
        if (pp_nals[i].i_type == NAL_SPS) {
            sps_len = pp_nals[i].i_payload - 4; // 去掉起是码
            memcpy(sps, pp_nals[i].p_payload + 4, sps_len);
//            if (javaCallHelper) {
//                javaCallHelper->callData(reinterpret_cast<char *>(pp_nals[i].p_payload), pp_nals[i].i_payload);
//            }
        } else if (pp_nals[i].i_type == NAL_PPS) {
            pps_len = pp_nals[i].i_payload - 4; // 去掉起是码
            memcpy(pps, pp_nals[i].p_payload + 4, pps_len);
            //发送pps  sps
            sendSpsPps(sps, pps, sps_len, pps_len);
//            if (javaCallHelper) {
//                javaCallHelper->callData(reinterpret_cast<char *>(pp_nals[i].p_payload), pp_nals[i].i_payload);
//            }
        } else {
//            if (javaCallHelper) {
//                javaCallHelper->callData(reinterpret_cast<char *>(pp_nals[i].p_payload), pp_nals[i].i_payload);
//            }
            setFrame(pp_nals[i].i_type, pp_nals[i].i_payload, pp_nals[i].p_payload);
        }
    }
}

void VideoChannel::sendSpsPps(uint8_t *sps, uint8_t *pps, int sps_len, int pps_len) {

    RTMPPacket *rtmpPacket = new RTMPPacket;

    int body_size = 13 + pps_len + pps_len;
    RTMPPacket_Alloc(rtmpPacket, body_size);

    int index = 0;

    rtmpPacket->m_body[index++] = 0x17;

    rtmpPacket->m_body[index++] = 0x00;
    rtmpPacket->m_body[index++] = 0x00;
    rtmpPacket->m_body[index++] = 0x00;
    rtmpPacket->m_body[index++] = 0x00;

    rtmpPacket->m_body[index++] = 0x01;
    rtmpPacket->m_body[index++] = sps[1];
    rtmpPacket->m_body[index++] = sps[2];
    rtmpPacket->m_body[index++] = sps[3];

    //固定写法
    rtmpPacket->m_body[index++] = 0xFF;
    rtmpPacket->m_body[index++] = 0xE1;
    //sps长度 2个字节 16位
    rtmpPacket->m_body[index++] = (sps_len >> 8) & 0xFF; // 取出高8位
    rtmpPacket->m_body[index++] = (sps_len) & 0xFF; // 低8位
    // 整个sps内容 没有直接index++ 方便后面做偏移
    memcpy(&rtmpPacket->m_body[index], sps, sps_len);
    index = index + sps_len;
//    LOGE("live->pps %s",pps);
    //  //pps  个数
    rtmpPacket->m_body[index++] = 0x01;
    // 长度 2个字节
    rtmpPacket->m_body[index++] = (pps_len >> 8) & 0xFF;
    rtmpPacket->m_body[index++] = (pps_len) & 0xFF;
    memcpy(&rtmpPacket->m_body[index], pps, pps_len);

    // 拼接成功 配置直播
    rtmpPacket->m_packetType = RTMP_PACKET_TYPE_VIDEO;
    rtmpPacket->m_nBodySize = body_size;
    rtmpPacket->m_nChannel = 0x04;
    rtmpPacket->m_hasAbsTimestamp = 0;

    // 系统赋值
    rtmpPacket->m_nTimeStamp = 0;

    rtmpPacket->m_headerType = RTMP_PACKET_SIZE_LARGE;
//    rtmpPacket->m_nInfoField2 = live->rtmp->m_stream_id;
//    LOGE("createSpsAndPpsPacket %d",rtmpPacket);
    sendData(rtmpPacket);
}

void VideoChannel::setFrame(int type, int payload, const uint8_t *p_payload) {

    // 去掉 00 00 00 01  /  00 00 01
    if (p_payload[2] == 0x00) {
        payload -= 4;
        p_payload += 4;
    } else if (p_payload[2] == 0x01) {
        payload -= 3;
        p_payload += 3;
    }

    RTMPPacket *packet = new RTMPPacket;

    int body_size = 9 + payload;
    RTMPPacket_Alloc(packet, body_size);

    // I 帧
    if (type == NAL_SLICE_IDR) {
//        LOGE("组装   I     帧");
        packet->m_body[0] = 0x17;
    } else {
        packet->m_body[0] = 0x27;
//        LOGE("组装其他帧");
    }
    packet->m_body[1] = 0x01;
    packet->m_body[2] = 0x00;
    packet->m_body[3] = 0x00;
    packet->m_body[4] = 0x00;

    // 4个字节长度  32位
    packet->m_body[5] = (payload >> 24) & 0xFF;
    packet->m_body[6] = (payload >> 16) & 0xFF;
    packet->m_body[7] = (payload >> 8) & 0xFF;
    packet->m_body[8] = (payload) & 0xFF;

    // cpy数据进去
    memcpy(&packet->m_body[9], p_payload, payload);

    packet->m_packetType = RTMP_PACKET_TYPE_VIDEO;
    packet->m_nBodySize = body_size;
    packet->m_nChannel = 0x04;
    packet->m_hasAbsTimestamp = 0;

    //系统赋值
//    packet->m_nTimeStamp = tms;

    //数据包大小 给服务器看
    packet->m_headerType = RTMP_PACKET_SIZE_LARGE;
//    packet->m_nInfoField2 = live->rtmp->m_stream_id;
    sendData(packet);

}


VideoChannel::~VideoChannel() {


    delete this->safeQueue;

}

void VideoChannel::sendData(RTMPPacket *packet) {

    if (packet) {
        packet->m_nTimeStamp = RTMP_GetTime() - startTime;
        this->safeQueue->push(packet);
    }

}

void *start(void *args) {


    VideoChannel *videoChannel = static_cast<VideoChannel *>(args);

    RTMP *rtmp = nullptr;
    LOGE("哈哈哈哈哈哈哈");

    rtmp = RTMP_Alloc();

    if (!rtmp) {
        LOGE("RTMP_Alloc  FAIL");
        return nullptr;
    }

    RTMP_Init(rtmp);
    rtmp->Link.timeout = 5;

    int ret = RTMP_SetupURL(rtmp, videoChannel->getRtmpUrl());

    if (!ret) {
        LOGE("RTMP_SetupURL  FAIL %s:", videoChannel->getRtmpUrl());
        return nullptr;
    }
    RTMP_EnableWrite(rtmp);

    ret = RTMP_Connect(rtmp, nullptr);
    if (!ret) {
        LOGE("RTMP_Connect  FAIL");
        return nullptr;
    }

    ret = RTMP_ConnectStream(rtmp, 0);

    if (!ret) {
        LOGE("RTMP_ConnectStream  FAIL");
        return nullptr;
    }
    LOGE("RTMP  SUCCESS %s:", videoChannel->getRtmpUrl());

    videoChannel->setStartTime(RTMP_GetTime());
    RTMPPacket *packet = nullptr;
    videoChannel->getSafeQueue()->setWork(1);
    while (true) {
        videoChannel->getSafeQueue()->pop(packet);
        if (!packet) {
            continue;
        }
        packet->m_nInfoField2 = rtmp->m_stream_id;
        // 发送包
        ret = RTMP_SendPacket(rtmp, packet, 1);

        if (!ret) {
            LOGE("RTMP_SendPacket FAIL");
            break;
        }
        LOGE("rtmp 发包成功");
    }
    if (packet) {
        RTMPPacket_Free(packet);
        delete packet;
        packet = nullptr;
    }
    if (rtmp) {
        RTMP_Close(rtmp);
        RTMP_Free(rtmp);
        rtmp = nullptr;
    }
    return nullptr;
}

void VideoChannel::startSendData() {

    char *url = new char[strlen(this->rtmpUrl) + 1];
    strcpy(url, rtmpUrl);
    this->safeQueue->setWork(1);
    pthread_create(&this->__pthread_ptr, nullptr, start, this);

    delete[] url;

}

char *VideoChannel::getRtmpUrl() {
    return this->rtmpUrl;
}

SafeQueue<RTMPPacket *> *VideoChannel::getSafeQueue() {
    return this->safeQueue;
}

void VideoChannel::setStartTime(uint32_t startTime) {

    this->startTime = startTime;
}

void VideoChannel::setRtmpUrl(char *url) {

    this->rtmpUrl = url;
}

void VideoChannel::setJavaCallHelper(JavaCallHelper *javaCallHelper) {

    this->javaCallHelper = javaCallHelper;
}
